﻿#ifndef CYCLONE_PARSER_BASE_H
#define CYCLONE_PARSER_BASE_H

#pragma once

#include "antlr4-runtime.h"
#include "syntax_error.h"
#include "macros.h"
#include "configurable_parser.hpp"

namespace cyclone::parser
{
    using antlr4::TokenStream;
    using antlr4::tree::ParseTree;
    using Testable = std::function<bool()>;
    using antlr4::Parser;
    using antlr4::ParserRuleContext;

    class DummyNode;
    class CycloneParserBase : public antlr4::Parser, public ConfigurableParser
    {
        struct CycloneParserBaseImpl;
        std::unique_ptr<CycloneParserBaseImpl> impl_;

    public:
        DISALLOW_COPY_AND_ASSIGN(CycloneParserBase);
        CycloneParserBase(TokenStream *input);
        virtual ~CycloneParserBase();
        // Semantic predications, try not to touch them
        bool p(std::string str);
        bool prev(std::string str);
        bool n(std::string str);
        bool next(std::string str);
        bool notLineTerminator();
        bool notOpenBraceAndNotFunction();
        bool closeBrace();
        bool here(int type);
        bool lineTerminatorAhead();

        template <typename... Tys>
        auto throwSyntaxError(SyntaxError &ex, const SourceCodeLocation &location, Tys... rest)
        {
            ex.location = location;
            throwSyntaxError(ex, rest...);
        }
        template <typename... Tys>
        auto throwSyntaxError(const std::string &info, Tys... rest)
        {
            SyntaxError ex(this, info);
            throwSyntaxError(ex, rest...);
        }
        template <typename... Tys>
        auto throwSyntaxError(const std::string &info, antlr4::Token *token, Tys... rest)
        {
            SyntaxError ex(this, info);
            if (token)
                ex.location = SourceCodeLocation{token->getLine(), token->getCharPositionInLine()};
            throwSyntaxError(ex, rest...);
        }

        template <typename... Tys>
        auto throwSyntaxError(SyntaxError &ex, const Testable &test, Tys... rest)
        {
            if (test())
                throwSyntaxError(ex, rest...);
        }

        auto throwSyntaxError(SyntaxError &ex)
        {
            throw ex;
        }
        /**
         * @brief return true if node is OptionalChain element ( MemberExpression / CallExpression )
         */
        static bool nodeIsOptionalChainElement(antlr4::ParserRuleContext *node);
        /**
         * @brief return true if node is in an OptionalChain
         */
        static bool nodeIsInOptionalChain(antlr4::ParserRuleContext *node);

    protected:
        void CycloneParserBase::enterClassDeclaration();

        void CycloneParserBase::leaveClassDeclaration();

        bool CycloneParserBase::isInClassDeclaration() const;
        /**
         * @brief Keep the variable declaration kind in the context for later use
         */
        void enterVariableDeclaration();
        /**
         * @brief Keep the variable declaration kind in the context for later use
         */
        void leaveVariableDeclaration();
        /**
         * @brief return true if the variable declaration is start with 'const'
         */
        bool variableDeclarationModifierIsConst() const;
        /**
         * @brief Judge if the parent context is expected type for pattern involved assignment expression
         */
        bool expectedAssignmentParent(antlr4::ParserRuleContext *node) const;
        /**
         * @brief e.g. Pattern = expression
         */
        bool isFollowedByAssignmentOperator() const;
        /**
         * @brief Judge if left expression is assignable e.g. MemberExpression
         */
        bool isAssignable(antlr4::ParserRuleContext *ctx) const;

        bool allowParenthesesInType_ = false;

        /**
         * Keyword is allowed to be used as identifier while paring the properties
         */
        bool isParsingProperties_ = false;

        /**
         *  Record if we are processing function | method | constructor , in which the identifier can be followed by typeAnnotation
         */
        bool isInParameter_ = false;

        void
        enableParenthesesInType(bool enable);

        /**
         * Used to valid if the regexFlags is valid
         */

        static constexpr auto ValidRegexFlags = "gimuysd";

        static constexpr auto NullishOperator = "??";

        void enterFunction();
        void leaveFunction();
        bool isInFunction() const;
        /**
         * @brief Use in loose mode
         */
        DummyNode *insertDummyNode();

        void enterPropertyKey();

        void leavePropertyKey();

        bool isParsingProperties();

        void
        enterParameter();

        void
        leaveParameter();

        bool
        canBeFollowedByTypeAnnotation();

        bool
        checkIsValidRegexFlag(
            const std::string &string);

        bool
        validateNullishOperatorMixedWithOtherLogicalOp(
            ParserRuleContext *ctx);

        void
        correctNewExpression(ParserRuleContext *ctx);
    };

} // namespace cyclone::parser

#endif
